package socks

import (
	"fmt"
	"io"
)

const (
	cDefaultHeaderSign = uint32(0xABCDEF99) // 默认头部标识
	cDefaultHeaderSize = 12
	cDefaultBuffSize   = 1024 * 8
)

type IMsgHeader interface {
	GetHeaderSize() int
	ReadHeader(br *BufferUtils) (isOk bool, bodyLen int)
	WriteHeader(bw *BufferUtils, bodyLen int) bool
}

type IHeaderCreator interface {
	CreateMsgHeader() IMsgHeader
}

type DefaultMsgHead struct {
	signature uint32
	bodyLen   uint32
	Cmd       uint32
}

func (d *DefaultMsgHead) GetHeaderSize() int {
	return cDefaultHeaderSize
}

func (d *DefaultMsgHead) ReadHeader(br *BufferUtils) (isOk bool, bodyLen int) {
	isOk = false
	bodyLen = 0
	if br.GetRemainLen() >= d.GetHeaderSize() {
		s := br.ReadUint32()
		if s == cDefaultHeaderSign {
			d.signature = s
			d.bodyLen = br.ReadUint32()
			d.Cmd = br.ReadUint32()
			isOk = true
			bodyLen = int(d.bodyLen)
		}
	}
	return isOk, bodyLen
}

func (d *DefaultMsgHead) WriteHeader(bw *BufferUtils, bodyLen int) bool {
	ret := false
	if bw.GetRemainLen() >= d.GetHeaderSize() {
		d.bodyLen = uint32(bodyLen)
		bw.Write(d.signature)
		bw.Write(d.bodyLen)
		bw.Write(d.Cmd)
		ret = true
	} else {
		show_log("size is too small：", bw.GetDataLen(), d.GetHeaderSize(), "remain = ", bw.GetRemainLen(), bw.pos)
	}
	return ret
}

func (d *DefaultMsgHead) CreateMsgHeader() IMsgHeader {
	return &DefaultMsgHead{signature: cDefaultHeaderSign}
}

type DefaultHeadHandler struct {
	head          IMsgHeader
	headIsOk      bool
	body          []byte
	bodyLen       int
	bodyWriteLen  int
	buff          []byte
	dataLen       int
	offset        int
	br            *BufferUtils // 读缓冲区
	writeHead     IMsgHeader   // 发送时需要的头部
	bw            *BufferUtils // 写缓冲区
	headerCreater IHeaderCreator
}

func NewDefaultHeadHandler(creater IHeaderCreator) *DefaultHeadHandler {
	if creater == nil {
		creater = &DefaultMsgHead{}
	}
	ret := &DefaultHeadHandler{head: creater.CreateMsgHeader(), headIsOk: false,
		buff: make([]byte, cDefaultBuffSize), br: NewBufferUtils(nil),
		headerCreater: creater, bw: NewBufferUtils(nil), writeHead: creater.CreateMsgHeader(),
	}
	ret.bw.ResetBuffSize(ret.head.GetHeaderSize())
	return ret
}

func (d *DefaultHeadHandler) resetBuffOffset() {
	if d.dataLen > 0 {
		copy(d.buff[d.offset:d.offset+d.dataLen], d.buff)
	}
	d.offset = 0
}

func (d *DefaultHeadHandler) ReadPack(reader io.Reader, conn SocketConnection) (pack *PackData) { // 判断包头是否合法，包头是否完整，并返回头部信息  // 不成功 readLen = 0
	br := d.br
	pack = nil
	for {
		br.Reset(d.buff[d.offset : d.offset+d.dataLen])
		if !d.headIsOk { // 头部还未接收完成
			if br.GetRemainLen() >= d.head.GetHeaderSize() {
				p := br.GetPos()
				if d.headIsOk, d.bodyLen = d.head.ReadHeader(br); d.headIsOk {
					d.body = make([]byte, d.bodyLen)
					d.bodyWriteLen = 0
				} else {
					br.ResetPos(p + 1) // 前移一个字节，继续试探
				}
			} else {
				readlen, err := reader.Read(d.buff[d.offset:])
				if (err != nil) && (err != io.EOF) {
					fmt.Println("socket read error: ", err)
					return nil // 接收出错，直接返回空，断开连接
				}
				d.dataLen += readlen // 头部的长度不够
			}
		} else if d.bodyWriteLen < d.bodyLen {
			if br.GetRemainLen() > 0 {
				n, _ := br.Read(d.body[d.bodyWriteLen:])
				d.bodyWriteLen += n
			} else {
				readlen, err := reader.Read(d.body[d.bodyWriteLen:])
				if err != nil {
					return nil
				}
				d.bodyWriteLen += readlen
			}
		}

		p := br.GetPos()
		if p > 0 {
			d.offset += p
			d.dataLen -= p

			if d.dataLen > 0 {
				if d.offset > cDefaultBuffSize/2 {
					d.resetBuffOffset()
				}
			} else { // 没有数据了，从头开始
				d.offset = 0
			}
		}

		if (d.bodyWriteLen >= d.bodyLen) && d.headIsOk {
			pack = &PackData{d.head, d.body}
			d.head = d.headerCreater.CreateMsgHeader()
			d.body = nil
			d.headIsOk = false
			break
		}
	}
	return pack
}

func (d *DefaultHeadHandler) WritePack(writer io.Writer, datas []byte) bool { // 写入包头信息
	bw := d.bw
	bw.ResetPos(0)
	dataLen := len(datas)
	if d.writeHead.WriteHeader(bw, dataLen) {
		if writeBuffer(writer, bw.bits) {
			if (dataLen == 0) || writeBuffer(writer, datas) {
				return true
			}
		} else {
			show_log("writeBuffer failed!!!")
		}
	} else {
		show_log("WritePack failed!!!")
	}
	return false
}
